using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using PDUConverter;

namespace Communication
{
    /// <summary>
    /// This class contains the handlers for unsolicited messages sent by the phone. It is for use by the GsmPhone
    /// class only and must not be made public.
    /// </summary>
    internal class MessageIndicationHandlers
    {
        private const string DeliverMemoryIndication = "\\+CMTI: \"(\\w+)\",(\\d+)";
        private const string DeliverMemoryIndicationStart = @"\+CMTI: ";
        private const string DeliverPduModeIndication = @"\+CMT: (\w*),(\d+)\r\n(\w+)";
        private const string DeliverPduModeIndicationStart = @"\+CMT: ";
        private readonly List<UnsoMessage> _messages = new List<UnsoMessage>();
        private const string StatusReportMemoryIndication = "\\+CDSI: \"(\\w+)\",(\\d+)";
        private const string StatusReportMemoryIndicationStart = @"\+CDSI: ";
        private const string StatusReportPduModeIndication = @"\+CDS: (\d+)\r\n(\w+)";
        private const string StatusReportPduModeIndicationStart = @"\+CDS: ";

        public MessageIndicationHandlers()
        {
            var item = new UnsoMessage("\\+CMTI: \"(\\w+)\",(\\d+)", HandleDeliverMemoryIndication)
                           {
                               StartPattern = @"\+CMTI: ",
                               Description = "New SMS-DELIVER received (indicated by memory location)"
                           };
            _messages.Add(item);
            var message2 = new UnsoMessage(@"\+CMT: (\w*),(\d+)\r\n(\w+)", HandleDeliverPduModeIndication)
                               {
                                   StartPattern = @"\+CMT: ",
                                   Description = "New SMS-DELIVER received (indicated by PDU mode version)",
                                   CompleteChecker = IsCompleteDeliverPduModeIndication
                               };
            _messages.Add(message2);
            var message3 = new UnsoMessage("\\+CDSI: \"(\\w+)\",(\\d+)", HandleStatusReportMemoryIndication)
                               {
                                   StartPattern = @"\+CDSI: ",
                                   Description = "New SMS-STATUS-REPORT received (indicated by memory location)"
                               };
            _messages.Add(message3);
            var message4 = new UnsoMessage(@"\+CDS: (\d+)\r\n(\w+)", HandleStatusReportPduModeIndication)
                               {
                                   StartPattern = @"\+CDS: ",
                                   Description = "New SMS-STATUS-REPORT received (indicated by PDU mode version)",
                                   CompleteChecker = IsCompleteStatusReportPduModeIndication
                               };
            _messages.Add(message4);
        }

        private static IMessageIndicationObject HandleDeliverMemoryIndication(ref string input)
        {
            var match = new Regex("\\+CMTI: \"(\\w+)\",(\\d+)").Match(input);
            if (!match.Success)
            {
                throw new ArgumentException("Input string does not contain an SMS-DELIVER memory location indication.");
            }
            var storage = match.Groups[1].Value;
            var index = int.Parse(match.Groups[2].Value);
            var location = new MemoryLocation(storage, index);
            input = input.Remove(match.Index, match.Length);
            return location;
        }

        private static IMessageIndicationObject HandleDeliverPduModeIndication(ref string input)
        {
            var match = new Regex(@"\+CMT: (\w*),(\d+)\r\n(\w+)").Match(input);
            if (!match.Success)
            {
                throw new ArgumentException("Input string does not contain an SMS-DELIVER PDU mode indication.");
            }
            var alpha = match.Groups[1].Value;
            var length = int.Parse(match.Groups[2].Value);
            var data = match.Groups[3].Value;
            var message = new ShortMessage(alpha, length, data);
            input = input.Remove(match.Index, match.Length);
            return message;
        }

        private static IMessageIndicationObject HandleStatusReportMemoryIndication(ref string input)
        {
            var match = new Regex("\\+CDSI: \"(\\w+)\",(\\d+)").Match(input);
            if (!match.Success)
            {
                throw new ArgumentException("Input string does not contain an SMS-STATUS-REPORT memory location indication.");
            }
            var storage = match.Groups[1].Value;
            var index = int.Parse(match.Groups[2].Value);
            var location = new MemoryLocation(storage, index);
            input = input.Remove(match.Index, match.Length);
            return location;
        }

        private static IMessageIndicationObject HandleStatusReportPduModeIndication(ref string input)
        {
            var match = new Regex(@"\+CDS: (\d+)\r\n(\w+)").Match(input);
            if (!match.Success)
            {
                throw new ArgumentException("Input string does not contain an SMS-STATUS-REPORT PDU mode indication.");
            }
            var length = int.Parse(match.Groups[1].Value);
            var data = match.Groups[2].Value;
            var message = new ShortMessage(string.Empty, length, data);
            input = input.Remove(match.Index, match.Length);
            return message;
        }

        /// <summary>
        /// Handles an unsolicited message of the specified input string.
        /// </summary>
        /// <param name="input">The input string to handle, the unsolicited message will be removed</param>
        /// <param name="description">Receives a textual description of the message, may be empty</param>
        /// <returns>The message indication object generated from the message</returns>
        /// <exception cref="T:System.ArgumentException">Input string does not match any of the supported
        /// unsolicited messages</exception>
        public IMessageIndicationObject HandleUnsolicitedMessage(ref string input, out string description)
        {
            foreach (var message in _messages)
            {
                if (!message.IsMatch(input)) continue;
                var obj2 = message.Handler(ref input);
                description = message.Description;
                return obj2;
            }
            throw new ArgumentException("Input string does not match any of the supported unsolicited messages.");
        }

        private static bool IsCompleteDeliverPduModeIndication(string input)
        {
            var match = new Regex(@"\+CMT: (\w*),(\d+)\r\n(\w+)").Match(input);
            if (match.Success)
            {
                var text1 = match.Groups[1].Value;
                var num = int.Parse(match.Groups[2].Value);
                var s = match.Groups[3].Value;
                if (BcdWorker.CountBytes(s) > 0)
                {
                    int @byte = BcdWorker.GetByte(s, 0);
                    var num3 = ((num * 2) + (@byte * 2)) + 2;
                    return (s.Length >= num3);
                }
            }
            return false;
        }

        private static bool IsCompleteStatusReportPduModeIndication(string input)
        {
            var match = new Regex(@"\+CDS: (\d+)\r\n(\w+)").Match(input);
            if (match.Success)
            {
                var num = int.Parse(match.Groups[1].Value);
                var s = match.Groups[2].Value;
                if (BcdWorker.CountBytes(s) > 0)
                {
                    int @byte = BcdWorker.GetByte(s, 0);
                    int num3 = ((num * 2) + (@byte * 2)) + 2;
                    return (s.Length >= num3);
                }
            }
            return false;
        }

        public bool IsIncompleteUnsolicitedMessage(string input)
        {
            if (_messages != null)
                foreach (var message in _messages)
                    if (message.IsStartMatch(input) && !message.IsMatch(input))
                        return true;
            return false;
        }

        public bool IsUnsolicitedMessage(string input)
        {
            foreach (var message in _messages)
                if (message.IsMatch(input))
                    return true;
            return false;
        }

        private delegate bool UnsoCompleteChecker(string input);

        private delegate IMessageIndicationObject UnsoHandler(ref string input);

        private class UnsoMessage
        {
            private UnsoCompleteChecker _completeChecker;
            private string _pattern;
            private string _startPattern;

            /// <summary>
            /// Initializes a new instance of the class.
            /// </summary>
            /// <param name="pattern">The regular expression pattern that must match to identify the unsolicited message.</param>
            /// <param name="handler">The method that handles the message if it matches the pattern</param>
            public UnsoMessage(string pattern, UnsoHandler handler)
            {
                _pattern = pattern;
                _startPattern = pattern;
                Description = string.Empty;
                Handler = handler;
                _completeChecker = null;
            }

            /// <summary>
            /// Checks if an input string matches the pattern.
            /// </summary>
            /// <param name="input">The string to check.</param>
            /// <returns>true if there is a match, false otherwise.</returns>
            /// <remarks>The input string is tested if it matches the pattern. If
            /// MessageIndicationHandlers.UnsoMessage.CompleteChecker is not null, this method is called instead of the default
            /// comparison.</remarks>
            public bool IsMatch(string input)
            {
                return _completeChecker != null ? _completeChecker(input) : Regex.IsMatch(input, _pattern);
            }

            /// <summary>
            /// Checks if an input string matches the start pattern.
            /// </summary>
            /// <param name="input">The string to check.</param>
            /// <returns>true if there is a match, false otherwise.</returns>
            public bool IsStartMatch(string input)
            {
                return Regex.IsMatch(input, _startPattern);
            }

            public UnsoCompleteChecker CompleteChecker
            {
                get
                {
                    return _completeChecker;
                }
                set
                {
                    _completeChecker = value;
                }
            }

            /// <summary>
            /// Gets or sets a textual description of the unsolicited message.
            /// </summary>
            public string Description { get; set; }

            /// <summary>
            /// Gets or sets the method that can handle the message if it matches the pattern.
            /// </summary>
            public UnsoHandler Handler { get; private set; }

            /// <summary>
            /// Gets or sets the regular expression pattern that must match to identify the unsolicited message.
            /// </summary>
            public string Pattern
            {
                get
                {
                    return _pattern;
                }
                set
                {
                    _pattern = value;
                }
            }

            /// <summary>
            /// Gets or sets the regular expression pattern that must match to identify the start of the unsolicited
            /// message. Is equal to MessageIndicationHandlers.UnsoMessage.Pattern if the message is sent always as a whole by the phone.
            /// </summary>
            public string StartPattern
            {
                get
                {
                    return _startPattern;
                }
                set
                {
                    _startPattern = value;
                }
            }
        }
    }
}